package com.bdi.mqtt_broker.config;

import com.bdi.mqtt_broker.annotation.PropertyFrom;
import com.bdi.mqtt_broker.constant.Constants;
import lombok.Data;
import org.springframework.beans.factory.config.YamlPropertiesFactoryBean;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.lang.reflect.Field;
import java.util.regex.Matcher;

/**
 * @author WangNing yogehaoren@gmial.com <br>
 * @since 1.0
 * 服务器主配置文件
 * ！！！ 与核心逻辑无关
 */
@Component
@Data
public class BrokerConfig {

    /**
     * MQTT代理 集群识别ID
     */
    @PropertyFrom("broker_id")
    private String brokerId = "Broker-1";

    /**
     * 服务器Port
     */
    @PropertyFrom("server.port")
    private Integer serverPort = 8118;


    /**
     * TCP 保活
     */
    @PropertyFrom("server.keepAlive")
    private Boolean keepAlive = true;

    /**
     * TCP 握手缓存
     */
    @PropertyFrom("server.soBacklog")
    private Integer soBacklog = 511;

    /**
     * redis Host
     */
    @PropertyFrom("server.redis.host")
    private String redisHost = "127.0.0.1";


    /**
     * redis Port
     */
    @PropertyFrom("server.redis.port")
    private Integer redisPort = 6379;

    /**
     * redis 最大连接数
     */
    @PropertyFrom("server.redis.maxActive")
    private Integer redisMaxActive = 1024;

    /**
     * redis 数据库
     */
    @PropertyFrom("server.redis.database")
    private Integer redisDatabase = 0;

    /**
     * redis 最大空闲连接数
     */
    @PropertyFrom("server.redis.maxIdle")
    private Integer redisMaxIdle = 20;

    /**
     * redis 获取可用连接的最大等待时间
     */
    @PropertyFrom("server.redis.maxWait")
    private Integer redisMaxWait = 1000;

    /**
     * redis 密码
     */
    @PropertyFrom("server.redis.password")
    private String redisPassword = "";


    @PropertyFrom("server.ssl.enable")
    private Boolean sslEnable = false;

    @PropertyFrom("server.ssl.crtPath")
    private String sslCrtPath;

    @PropertyFrom("server.ssl.keyPath")
    private String sslKeyPath;

    @PropertyFrom("server.ssl.provider")
    private String sslProvider = "jdk";

    /**
     * 从配置文件中加载配置
     */
    @PostConstruct
    public void initBrokerConfig() throws IllegalAccessException {
        YamlPropertiesFactoryBean yaml = new YamlPropertiesFactoryBean();
        yaml.setResources(new ClassPathResource("broker-config.yaml"));
        assert yaml.getObject() != null;

        Class<BrokerConfig> configClazz = BrokerConfig.class;

        Field[] declaredFields = configClazz.getDeclaredFields();

        for (Field field: declaredFields){

            PropertyFrom annotation = field.getAnnotation(PropertyFrom.class);
            if(annotation != null){

                String from = annotation.value();
                if(!"".equals(from)){
                    String propertyValue = yaml.getObject().getProperty(from);



                    if(propertyValue!=null && !"".equals(propertyValue)){

                        // 抽取环境变量
                        if(propertyValue.matches("\\$\\{(.*)\\}")){
                            Matcher matcher = Constants.ENVIRONMENT_PATTERN.matcher(propertyValue);
                            if(matcher.find()){
                                String environmentVariable = matcher.group(1).trim();
                                propertyValue = System.getenv(environmentVariable);
                                if(propertyValue==null||"".equals(propertyValue)){
                                    throw new RuntimeException("Broker Server Can't find Environment "+environmentVariable);
                                }
                            }
                        }

                        field.setAccessible(true);
                        if(field.getType() == Integer.class){
                            field.set(this, Integer.parseInt(propertyValue));
                        }else if(field.getType() == Boolean.class){
                            field.set(this, Boolean.parseBoolean(propertyValue));
                        }else if(field.getType() == Double.class){
                            field.set(this, Double.parseDouble(propertyValue));
                        }else if(field.getType() == String.class){
                            field.set(this, propertyValue);
                        }

                    }
                }
            }
        }

    }

}
